#include <boost/asio.hpp>
#include <nlohmann/json.hpp>
#include <iostream>
#include <string>
#include <memory>

using boost::asio::ip::tcp;
using json = nlohmann::json;

class Client : public std::enable_shared_from_this<Client> {
public:
    Client(boost::asio::io_context& io_context)
        : socket_(io_context) {
    }

    void connect(const std::string& host, const std::string& port) {
        auto self(shared_from_this());
        tcp::resolver resolver(socket_.get_executor());
        auto endpoints = resolver.resolve(host, port);
        
        boost::asio::async_connect(socket_, endpoints,
            [this, self](boost::system::error_code ec, tcp::endpoint) {
                if (!ec) {
                    std::cout << "Connected to server!" << std::endl;
                    do_read();
                } else {
                    std::cout << "Connection failed: " << ec.message() << std::endl;
                }
            });
    }

private:
    void do_read() {
        auto self(shared_from_this());
        boost::asio::async_read_until(socket_, data_, '\n',
            [this, self](boost::system::error_code ec, std::size_t length) {
                if (!ec) {
                    std::string message(boost::asio::buffers_begin(data_.data()),
                                      boost::asio::buffers_begin(data_.data()) + length);
                    data_.consume(length);

                    try {
                        auto j = json::parse(message);
                        std::cout << "Received from server: " << j.dump(2) << std::endl;

                        // Send hello response back to server
                        json response = {
                            {"type", "hello_response"},
                            {"message", "Hello from client!"}
                        };
                        std::string msg = response.dump() + "\n";
                        
                        boost::asio::async_write(socket_, boost::asio::buffer(msg),
                            [this, self](boost::system::error_code ec, std::size_t /*length*/) {
                                if (!ec) {
                                    do_read();  // Continue reading for more messages
                                }
                            });

                    } catch (const json::parse_error& e) {
                        std::cerr << "JSON parse error: " << e.what() << std::endl;
                    }
                }
            });
    }

    tcp::socket socket_;
    boost::asio::streambuf data_;
};

int main() {
    try {
        boost::asio::io_context io_context;
        auto client = std::make_shared<Client>(io_context);
        client->connect("127.0.0.1", "12345");
        io_context.run();
    } catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }

    return 0;
}